浏览器功能定制1:将网页内容保存为 Markdown

通过前段时间阅读 qutebrowser 代码,有了一些初步了解,开始尝试进行二次开发,实现自己喜欢的功能。

网页归档功能对我而言尤为重要。目前市面上存在许多优秀工具,比如 singlefile、scrapbook 等。包括浏览器自己也具备 MHTML 保存归档功能。

这些工具的好处,可以将网页按原貌保存。有的还提供裁剪功能,像剪报一样,裁剪自己喜欢的部分。(可能很多年轻人已经不知道什么是剪报了)

在本文中,我想更进一步,直接将网页保存为 Markdown,存入 Obsidian 中。舍弃掉样式,这是我更加希望的。

在本文中,我将记录探索过程,以及基于 qutebrowser 定制的过程。但是我没有计划开源,原因是我所基于的环境过于特殊,不具备易用性。本文可以提供一些思路,供浏览器插件开发者、Geeks 参考,相当于抛砖引玉。

调研:Html 转 Markdown

html2text

Convert HTML to Markdown-formatted text. 这个库已经 11 年没有更新了。

GitHub 首页:aaronsw/html2text: Convert HTML to Markdown-formatted text. (github.com)

Obsidian Importer

这是 Obsidian 官方的笔记导入插件,方便用户从其它笔记系统迁入 Obsidian,其中就包含 HTML 导入支持。

具体 HTML 导入代码位于这里。核心一步 htmlToMarkdown 是调用 Obsidian 的 API (在帖子中,提到具体是基于 turndown 实现的)完成。

GitHub 首页:obsidianmd/obsidian-importer: Obsidian Importer lets you import notes from other apps and file formats into your Obsidian vault. (github.com)

turndown

看起来是 Obsidian htmlToMarkdown 的底层实现库,有 7.4k star,功能更加强大与完善,是一个 JavaScript 库。

由于我是用 Python 生态,Python 下要是有类似库就好了。

GitHub 首页:mixmark-io/turndown: 🛏 An HTML to Markdown converter written in JavaScript (github.com)

将turndown项目改造成CLI版本秒杀Python版的html2text | 阿甘博客 (sharpgan.com)》这篇文章,从题目中即可了解到,turndown 比 html2text 要强大得多。

python-markdownify

How to Convert HTML to Markdown in Python (brianli.com)》一文中提到的库。

这个库使用起来比较简单,好在扩展性也比较强大,比较容易定制。

GitHub 首页:matthewwithanm/python-markdownify: Convert HTML to Markdown (github.com)

调研:网页正文提取

GNE

GitHub 3.2k star

项目中提出一种基于可视化区域提高识别准确度,先执行一段 JavaScript 脚本,将每个元素的坐标存在标签内,然后根据坐标识别是否是网页正文。

GitHub 首页:GeneralNewsExtractor/GeneralNewsExtractor: 新闻网页正文通用抽取器 Beta 版.

文档:GNE: 通用新闻网站正文抽取器 — GeneralNewsExtractor 0.0.6 文档

newspaper

GitHub 13.2k star,有3、4年没有维护了。

News, full-text, and article metadata extraction in Python 3. Advanced docs:

GitHub 首页:codelucas/newspaper: News, full-text, and article metadata extraction in Python 3. Advanced docs:

文档:Newspaper3k: Article scraping & curation — newspaper 0.0.2 documentation

Python-Goose

3.9k star,有快10年没有维护了。

GitHub 首页:Python-Goose

goose3

A Python 3 compatible version of goose http://goose3.readthedocs.io/en/latest/index.html

仍在活跃维护中,star 715。

A Python 3 compatible version of goose http://goose3.readthedocs.io/en/latest/index.html

介绍文章《爬虫万金油,一鹅在手,抓遍全球:goose 简介 - 知乎

html-extractor

《基于行块分布函数的通用网页正文抽取算法》的代码实现改进。

cnyangkui/html-extractor: 基于行块分布函数的通用网页正文抽取算法优化,Python实现

python-boilerpipe

python-readability

实现思路

通过 qutebrowser 命令触发,比如 save-markdown

网页正文提取

在《浏览器功能定制2:显示网页正文的阅读模式》中,我已经实现了一个简版的网页正文提取功能,并实现了一种简版阅读模式,通过 main-content 命令触发。

我准备按照如下工作流:打开网页,main-content 提取正文 HTML、save-markdown 进行保存。中间两个命令采用手动触发的方式。

Obsidian 目录创建

接下来开始探索 save-markdown 的实现思路。首先,我需要根据页面的 url,去 Obsidian 中创建目录。

首先,Obsidian 中有一个 base 目录,比如 999.沧海拾珍自动化

在该目录下,按照域名(去掉 www. 前缀)创建目录。如果目录已存在,则不要重复创建。输入是 QUrl 的 URL 地址,返回一个 Python pathlib 的目录,共我后续基于这个目录派生出各个文件。

保存网页中所有图片

这里来了一个棘手的问题,网页正文中的图片,我希望离线缓存到笔记目录当中,做到彻底离线化。

怎么实现呢?Qt 并没有提供 API,从网页中 get 图片(如果有请告诉我!)。一种方法是通过 requsts 再请求一次图片,但我不希望这样。

为了解决这个问题,我给 qutebrowser 加了一层 mitmproxy,具体方法参见《浏览器功能定制3:使用 mitmproxy 增强浏览器的拦截能力》。

总体来说,所有请求中的图片,都将被缓存到一个 Python 字典中。这样,在我离线保存网页的时候,只需要向这个字典中进行查询即可。

小插曲:多进程问题:遇到了一个新问题,mitmproxy 运行在单独进程里,图片缓存字典,在主进程里获取不到。解决方案:将 mitmproxy 从多进程改为多线程,图片缓存可以在跨线程间共享。

HTML 转 Markdown

Step1:加载站点适配规则

我没有设计什么神奇的通用算法,而是针对不同的站点,预置了一些适配规则。

适配规则包括:

有的甚至可以生成新的网页模板,把内容抠出来,放到新网页模板中去。

基于浏览器的底层 API,不论是模拟操作,还是元素操作,都开启上帝视角,随心所欲。

提取网页正文

实际操作,发现网站众多,光靠匹配还不够。

给出一个文章页,如何识别出最核心的那个 div tag 呢?

Step2:获取渲染后的 HTML

获取渲染后的 HTML,这时 HTML 已经是 ready for markdown 的了。

如果还需要 Hack,适配规则在这里还有一个 hook 时机。

Step3:Html2Markdown

调用 python-markdownify,使用定制的转换器,执行一些更加完美的处理。

如果还需要 Hack,适配规则在这里还有一个 hook 时机。

Step4:拷入 Obsidian

我在 Obsidian 设计了一个专门的文件目录布局:

其中:

这样,后续如果要移动目录,只需要三个文件一起移动即可。并且由于名称开头相同,他们在文件管理器中也可以一起出现。

Step5:Schema 自动 Obsidian 跳转

保存完成后,触发 Obsidian 自动打开笔记。在笔记中进行阅读、学习。

这个功能叫 Obsidian URL(Obsidian 链接),格式:obsidian://open?省略

只要拼凑出这个链接,通过 Python 触发就可以了。

其它

灵感

在网页正文提取这个主题上,先用已有的库,实现足够多的 html -> Markdown 之后,作为训练集训练一个 LLM。

DEBUG 选项

预留一个 Debug 开关,当开启时,将每一步的中间结果进行保存,方便开一个编辑器,进行调试、检查。

网络资源


本文作者:Maeiee

本文链接:浏览器功能定制1:将网页内容保存为 Markdown

版权声明:如无特别声明,本文即为原创文章,版权归 Maeiee 所有,未经允许不得转载!


喜欢我文章的朋友请随缘打赏,鼓励我创作更多更好的作品!